#include "setup.h"

// 寄存器位置
#define REG_SECONDS 0x80 // 秒
#define REG_MINUTES 0x82 // 分
#define REG_HOUR 0x84    // 时
#define REG_DATE 0x86    // 日
#define REG_MONTH 0x88   // 月
#define REG_DAY 0x8A     // 星期
#define REG_YEAR 0x8C    // 年
#define REG_WP 0x8E      // 控制寄存器
#define REG_BURST 0xBE   // 时钟多字节方式


bool isHalted()
{
      pinMode(DS1302_DATA, OUTPUT);
      digitalWrite(DS1302_RST, HIGH);
      uint8_t command = 0b10000001 | REG_SECONDS;
      for (uint8_t b = 0; b < 8; b++)
      {
            digitalWrite(DS1302_DATA, (command & 0x01) ? HIGH : LOW);
            digitalWrite(DS1302_SCK, HIGH);
            delayMicroseconds(1);
            digitalWrite(DS1302_SCK, LOW);
            command >>= 1;
      }
      pinMode(DS1302_DATA, INPUT);
      uint8_t seconds = 0;
      for (uint8_t b = 0; b < 8; b++)
      {
            if (digitalRead(DS1302_DATA) == HIGH)
                  seconds |= 0x01 << b;
            digitalWrite(DS1302_SCK, HIGH);
            delayMicroseconds(1);
            digitalWrite(DS1302_SCK, LOW);
      }
      digitalWrite(DS1302_RST, LOW);
      return (seconds & 0b10000000);
}



uint8_t _readByte()
{
      uint8_t byte = 0;

      for (uint8_t b = 0; b < 8; b++)
      {
            if (digitalRead(DS1302_DATA) == HIGH)
                  byte |= 0x01 << b;
            digitalWrite(DS1302_SCK, HIGH);
            delayMicroseconds(1);
            digitalWrite(DS1302_SCK, LOW);
      }
      return byte;
}
void _writeByte(uint8_t value)
{
      for (uint8_t b = 0; b < 8; b++)
      {
            digitalWrite(DS1302_DATA, (value & 0x01) ? HIGH : LOW);
            digitalWrite(DS1302_SCK, HIGH);
            delayMicroseconds(1);
            digitalWrite(DS1302_SCK, LOW);
            value >>= 1;
      }
}
uint8_t _bcd2dec(uint8_t bcd)
{
      return ((bcd / 16 * 10) + (bcd % 16));
}
void getDateTime(DateTime *dt)
{
      pinMode(DS1302_DATA, OUTPUT);
      digitalWrite(DS1302_RST, HIGH);
      uint8_t command = 0b10000001 | REG_BURST;
      _writeByte(command);
      pinMode(DS1302_DATA, INPUT);

      dt->second = _bcd2dec(_readByte() & 0b01111111);
      dt->minute = _bcd2dec(_readByte() & 0b01111111);
      dt->hour = _bcd2dec(_readByte() & 0b00111111);
      dt->day = _bcd2dec(_readByte() & 0b00111111);
      dt->month = _bcd2dec(_readByte() & 0b00011111);
      dt->dow = _bcd2dec(_readByte() & 0b00000111);
      dt->year = _bcd2dec(_readByte() & 0b01111111);
      digitalWrite(DS1302_RST, LOW);
}

uint8_t _dec2bcd(uint8_t dec)
{
      return ((dec / 10 * 16) + (dec % 10));
}

void setDateTime(DateTime *dt)
{
      pinMode(DS1302_DATA, OUTPUT);
      digitalWrite(DS1302_RST, HIGH);
      uint8_t command = 0b10000000 | REG_WP;
      _writeByte(command);

      _writeByte(0b00000000);
      digitalWrite(DS1302_RST, LOW);

      digitalWrite(DS1302_RST, HIGH);
      command = 0b10000000 | REG_BURST;
      _writeByte(command);

      _writeByte(_dec2bcd(dt->second % 60));
      _writeByte(_dec2bcd(dt->minute % 60));
      _writeByte(_dec2bcd(dt->hour % 24));
      _writeByte(_dec2bcd(dt->day % 32));
      _writeByte(_dec2bcd(dt->month % 13));
      _writeByte(_dec2bcd(dt->dow % 8));
      _writeByte(_dec2bcd(dt->year % 100));
      _writeByte(0b10000000);
      digitalWrite(DS1302_RST, LOW);
      pinMode(DS1302_DATA, INPUT);
}

void Ds1302_init()
{
      pinMode(DS1302_RST, OUTPUT);
      pinMode(DS1302_SCK, OUTPUT);
      pinMode(DS1302_DATA, INPUT);

      digitalWrite(DS1302_RST, LOW);
      digitalWrite(DS1302_SCK, LOW);

      if (isHalted())
      {
            DateTime dt = {
                .year = 23,
                .month = MONTH_NOV,
                .day = 15,
                .hour = 15,
                .minute = 51,
                .second = 30,
                .dow = DOW_WED};

            setDateTime(&dt);
      }
}

void state_led_init()
{
      pinMode(M74HC595_SCK, OUTPUT);
      pinMode(M74HC595_CLK, OUTPUT);
      pinMode(M74HC595_MOSI, OUTPUT);
      digitalWrite(M74HC595_SCK, HIGH);
      digitalWrite(M74HC595_CLK, LOW);
      digitalWrite(M74HC595_MOSI, LOW);
}

void state_led_sendData(uint8_t data)
{
      digitalWrite(M74HC595_SCK, HIGH); // 时钟信号置高
      for (uint8_t i = 0; i < 8; i++)
      {
            digitalWrite(M74HC595_CLK, LOW); // 时钟信号置低
            if (data & 0x80)
                  digitalWrite(M74HC595_MOSI, HIGH); // 输出数据为1
            else
                  digitalWrite(M74HC595_MOSI, LOW); // 输出数据为0
            data <<= 1;
            digitalWrite(M74HC595_CLK, HIGH); // 时钟信号置高，数据移位
      }                                       // 发送数据
      digitalWrite(M74HC595_SCK, LOW);        // 时钟信号置低
}
